
<template>
  <div ref="con" class="contents" @scroll="onScroll">
    <div class="masonry-container">
      <div class="item" v-for="(item, index) in list" :key="item">
        <img class="min-img" :src="item" @click="chooseOne(item)" />
      </div>
    </div>
  </div>
</template>

<script> 
/*
 本页数据原理
 加载数据json ['','']
 然后 缩略图地址换为大图地址
 下载和应用
 */

import Masonry from 'masonry-layout';
import { showToast, closeToast } from 'vant';
import { debounce } from '../utils';
export default {
  name: 'Wallpaper',
  data() {
    return {
      cur:'',
      //blocks: Array(100).fill(1),
      list: [],
      unloadedList: [],
      show: false,
      msnry: null,
      isLoading: false,
      previousScrollPosition: 0,
      scroll: 0, //记录浏览位置
    };
  },
  beforeRouteEnter(to, from, next) {
    next(vm => {
      const scrollBar = document.getElementsByClassName('contents')[0]
      vm.msnry?.layout()
      scrollBar.scrollTop = vm.scroll
    })
  },
  beforeRouteLeave(to, from, next) {
    closeToast()
    const scrollBar = document.getElementsByClassName('contents')[0]
    this.scroll = scrollBar.scrollTop
    next()
  },
  created() {
    this.load();
  },
  methods: {
    load() {
      const that = this; 
      fetch("https://ThemeCenter.github.io/img.json")
        .then((response) => {
          return response.json();
        })
        .then(async (data) => {
          // 先展示 10 张壁纸
          // 为了避免图片加载过程中Masonry尚未重新布局导致错乱的页面展示效果
          // 使用图片预加载的设计，然后再将图片渲染到页面中
          that.unloadedList = data.map((e) => `https://ThemeCenter.github.io/Wallpapers-small/` + (e.replace("wallpapers/",'')))
          let srcList = that.unloadedList.splice(0, this.$route.query.imgnum ? this.$route.query.imgnum : 10);
          try{
            await that.preloadImages(srcList);
          } catch(err) {}
          that.list = srcList;
          // 等待图片加载完成  Masonry排列
          that.$nextTick(async () => {
            let ele = document.querySelector('.masonry-container');
            that.msnry = new Masonry(ele, { itemSelector: '.item', gutter: 2, transitionDuration: 0 })
            that.msnry.layout()
            if(that.$route.query.scrolltop) document.getElementsByClassName('contents')[0].scrollTop = this.$route.query.scrolltop
            // 填满整页
            this.checkAndFillPage();
          })
        })
        .catch((error) => {
          console.error("Error fetching data:", error);
        });
    },
    preloadImages(srcList) {
      const images = new Array();
      const promises = new Array();
      srcList.forEach(src => {
        const img = new Image()
        const promise = new Promise((resolve, reject) => {
          img.onload = () => { resolve(true) };
          img.onerror = () => { reject(false) }
        })
        img.src = src
        images.push(img)
        promises.push(promise)
      })
      return Promise.all(promises)
    },
    chooseOne(src){
      this.$router.push({ path: '/imagepreview', query: {  curUrl: src.replace("-small","") } })
    },
    async checkAndFillPage() {
      // 开始检测
      if(this.checkEnd() && this.unloadedList.length !== 0) {
        let fiveSrc = this.unloadedList.splice(0, 5)
        try {
          await this.preloadImages(fiveSrc)
        } catch(err) {
          // console.log(err)
        }
        this.list = this.list.concat(fiveSrc)
        // Masonry布局
        await new Promise((resolve) => { this.$nextTick(() => { resolve() }) })
        // const lastFive = document.querySelectorAll('.item:nth-last-child(-n+5)')
        // that.msnry.appended(lastFive)
        this.msnry.reloadItems()
        this.msnry.layout()
        await new Promise((resolve) => { this.$nextTick(() => { resolve() }) })
        this.checkAndFillPage()
      } else {
        closeToast()
      }
    },
    checkEnd() {
      // 检查最后1张图片顶部是否超出页面底部过一屏，若没有则返回ture;
      // 需要加载5张图片，待图片加载完后，Masonry布局，继续检查
      // 向下滚动时触发检查
      // 页面加载时触发检查
      // 检查返回ture时继续加载完后继续检查（除非所有图片已加载，则不继续调用），直到返回false。
      const lastPic = document.querySelector('.item:last-child')
      const windowHeight = window.innerHeight
      const rect = lastPic.getBoundingClientRect()
      const picTop = rect.top
      // 判断pic是否填满当前页，填满则取消toast提示
      if(picTop >= windowHeight) closeToast()
      if(picTop <= windowHeight * 2) return true
      return false
    },
    recordScroll: debounce((vm, currentScrollPosition) => {
      vm.$router.replace({
        query: { imgnum: vm.list.length, scrolltop: currentScrollPosition }
      })
    }, 300),
    async onScroll(event) {
      let currentScrollPosition = event.target.scrollTop; // 当前滚动位置
      // 记录scrollTop，防止修改路由过于频繁，使用防抖
      this.recordScroll(this, currentScrollPosition)
      let isScrollToEnd = event.target.scrollHeight - currentScrollPosition <= (event.target.clientHeight + 8)
      // 向下滚动时才做判断
      if (currentScrollPosition > this.previousScrollPosition) {
        // 滚动条触底，图片加载完毕，则提示
        if (this.unloadedList.length === 0) {
          if (isScrollToEnd) {
            showToast({ message: '已经到底了', position: 'bottom' })
          }
        } else {
          if (isScrollToEnd) {
            showToast({ message: '图片加载中...', position: 'bottom', duration: 0 })
          }
          if (!this.isLoading) {
            this.isLoading = true;
            try {
              await this.checkAndFillPage();// 检测图片触底并加载
            } catch(err) {
              // console.log(err)
            }
            this.isLoading = false;
          }
        }
      }
      this.previousScrollPosition = currentScrollPosition; // 更新滚动位置
    },
  },
};
</script>

<style>
.contents {
  padding: 8px;
  width: 100%;
  box-sizing: border-box;
  height: 100vh;
  overflow: auto;
}
.item {
  width: calc((100% - 2px) / 2);
  position: relative;
  overflow: hidden;
  margin-bottom: 2px;
  cursor: pointer;
}
.item:hover>img {
  transform: scale(1.1);
}
.item>img{
  transition: .3s ease-out;
}
@media screen and (max-width: 1080px) {
  .item {
    width: calc((100% - 2px) / 2);
  }
}
@media Screen and (min-width: 1081px) and (max-width: 1500px) {
  .item {
    width: calc((100% - 4px) / 3);
  }
}
@media screen and (min-width: 1501px) and (max-width: 1850px) {
  .item {
    width: calc((100% - 6px) / 4);
  }
}
@media screen and (min-width: 1851px) {
  .item {
    width: calc((100% - 8px) / 5);
  }
}
.min-img {
  width: 100%;
  vertical-align:bottom;
}
</style>

 